package org.Yiran.timetale_re.util.geometry;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.phys.shapes.Shapes;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 模型JSON体素形状解析器
 * <p>
 * 用于从Minecraft模型JSON文件中解析并创建VoxelShape，支持根据方块朝向进行坐标变换。
 * 提供缓存机制以提高性能，并支持复杂形状的合并处理。
 * </p>
 * 
 * @author Yiran
 * @version 1.0
 */
public class ModelJsonVoxelShapeParser {

    // ==================== 缓存定义 ====================
    
    /**
     * 全局模型缓存，避免重复解析相同模型文件
     * <p>
     * 键为"modId:modelPath"格式的字符串，值为模型元素数据列表。
     * </p>
     */
    private static final Map<String, List<ElementData>> modelCache = new ConcurrentHashMap<>();
    
    /**
     * 全局形状缓存
     * <p>
     * 键为"modId:modelPath"格式的字符串，值为方向到体素形状的映射。
     * </p>
     */
    private static final Map<String, Map<Direction, VoxelShape>> shapeCache = new ConcurrentHashMap<>();
    
    /**
     * BlockState信息缓存
     * <p>
     * 键为"modId:blockName"格式的字符串，值为变体键到方向的映射。
     * </p>
     */
    private static final Map<String, Map<String, Direction>> blockStateCache = new ConcurrentHashMap<>();

    // ==================== 配置常量 ====================
    
    /**
     * 调试模式开关
     * <p>
     * 启用时会输出解析过程中的调试信息。
     * </p>
     */
    private static final boolean DEBUG = true;

    // ==================== 形状创建方法 ====================

    /**
     * 从模型JSON文件创建VoxelShape
     * <p>
     * 解析指定的模型JSON文件，根据方块朝向创建对应的VoxelShape。
     * 支持缓存机制以提高性能，避免重复解析相同模型。
     * </p>
     * 
     * @param modId 模组ID
     * @param modelPath 模型路径（相对于assets目录）
     * @param direction 方块朝向
     * @param blockName 方块名称
     * @return VoxelShape 体素形状
     */
    public static VoxelShape createShapeFromModel(String modId, String modelPath, Direction direction, String blockName) {
        try {
            String cacheKey = modId + ":" + modelPath;
            Direction adjustedDirection = getAdjustedDirection(modId, blockName, direction);

            // 检查全局形状缓存
            Map<Direction, VoxelShape> dirCache = shapeCache.computeIfAbsent(cacheKey, k -> new ConcurrentHashMap<>());
            if (dirCache.containsKey(adjustedDirection)) {
                return dirCache.get(adjustedDirection);
            }

            // 获取模型数据
            List<ElementData> elements = getModelElements(modId, modelPath);

            if (elements.isEmpty()) {
                VoxelShape emptyShape = Shapes.block();
                dirCache.put(adjustedDirection, emptyShape);
                return emptyShape;
            }

            List<VoxelShape> shapes = new ArrayList<>();

            // 遍历所有元素
            for (ElementData element : elements) {
                // 应用朝向变换
                double[] transformedFrom = transformPoint(element.from, adjustedDirection);
                double[] transformedTo = transformPoint(element.to, adjustedDirection);

                // 创建基础形状
                VoxelShape shape = VoxelShapeHelper.createVoxelShape(transformedFrom, transformedTo);
                shapes.add(shape);
            }

            // 合并所有形状
            VoxelShape result = VoxelShapeHelper.combineShapes(shapes);
            for (VoxelShape shape : shapes) {
                result = Shapes.join(result, shape, BooleanOp.OR);
            }
            dirCache.put(adjustedDirection, result);
            if (DEBUG) {
                System.out.println("模型名称: " + modelPath);
                System.out.println("原始模型数量: " + shapes.size());
                System.out.println("合并的形状部分: " + result.toAabbs().size());
                System.out.println("是否为简单形状: " + (result.toAabbs().size() <= 1));
                System.out.println();
            }
            return result;
        } catch (Exception e) {
            return Shapes.block(); // 静默处理错误，返回默认形状
        }
    }

    // ==================== 方向处理方法 ====================

    /**
     * 获取调整后的方向（根据blockstate文件）
     * <p>
     * 根据BlockState文件中的信息调整方块的朝向。
     * </p>
     * 
     * @param modId 模组ID
     * @param blockName 方块名称
     * @param originalDirection 原始方向
     * @return 调整后的方向
     */
    private static Direction getAdjustedDirection(String modId, String blockName, Direction originalDirection) {
        if (blockName == null || blockName.isEmpty()) {
            return originalDirection;
        }

        String blockStateKey = modId + ":" + blockName;
        // 使用 computeIfAbsent 确保只解析一次
        blockStateCache.computeIfAbsent(blockStateKey, k -> BlockStateParser.parseBlockState(modId, blockName));

        return originalDirection;
    }

    // ==================== 模型元素处理方法 ====================

    /**
     * 检查元素是否包含旋转
     * <p>
     * 检查模型元素是否包含旋转属性，如果包含则返回true。
     * 包含旋转的元素将被跳过，因为它们难以正确转换为碰撞箱。
     * </p>
     * 
     * @param elementObj 元素JSON对象
     * @return 如果元素包含旋转则返回true，否则返回false
     */
    private static boolean hasRotation(JsonObject elementObj) {
        if (!elementObj.has("rotation")) {
            return false;
        }

        JsonObject rotation = elementObj.getAsJsonObject("rotation");
        if (rotation == null) {
            return false;
        }

        // 检查旋转属性是否存在且不为0
        boolean hasXRotation = rotation.has("angle") && rotation.get("angle").getAsDouble() != 0;
        boolean hasYRotation = rotation.has("y") && rotation.get("y").getAsDouble() != 0;
        boolean hasZRotation = rotation.has("z") && rotation.get("z").getAsDouble() != 0;

        return hasXRotation || hasYRotation || hasZRotation;
    }

    /**
     * 获取模型元素数据（带缓存）
     * <p>
     * 从指定模型文件中解析元素数据，并使用缓存机制避免重复解析。
     * 跳过包含旋转的元素，因为它们难以正确转换为碰撞箱。
     * </p>
     * 
     * @param modId 模组ID
     * @param modelPath 模型路径
     * @return 模型元素数据列表
     */
    private static List<ElementData> getModelElements(String modId, String modelPath) {
        String cacheKey = modId + ":" + modelPath;

        // 直接返回缓存结果，避免重复计算
        if (modelCache.containsKey(cacheKey)) {
            return modelCache.get(cacheKey);
        }

        List<ElementData> elements = new ArrayList<>();

        try {
            // 构建资源路径
            String resourcePath = "/assets/" + modId + "/" + modelPath;

            // 加载JSON文件
            InputStream inputStream = ModelJsonVoxelShapeParser.class.getResourceAsStream(resourcePath);
            if (inputStream == null) {
                modelCache.put(cacheKey, elements);
                return elements;
            }

            // 解析JSON (兼容新版本Gson)
            JsonObject jsonObject = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject();

            // 获取elements数组
            JsonArray elementsArray = jsonObject.getAsJsonArray("elements");
            if (elementsArray == null) {
                modelCache.put(cacheKey, elements);
                return elements;
            }

            // 遍历所有元素
            for (JsonElement element : elementsArray) {
                JsonObject elementObj = element.getAsJsonObject();

                // 检查是否有旋转属性
                boolean hasRotation = hasRotation(elementObj);

                // 如果有旋转，则跳过该元素的碰撞箱生成
                if (hasRotation) {
                    continue;
                }

                // 解析from和to数组
                JsonArray fromArray = elementObj.getAsJsonArray("from");
                JsonArray toArray = elementObj.getAsJsonArray("to");

                if (fromArray != null && toArray != null && fromArray.size() == 3 && toArray.size() == 3) {
                    double[] from = jsonArrayToDoubleArray(fromArray);
                    double[] to = jsonArrayToDoubleArray(toArray);
                    elements.add(new ElementData(from, to, hasRotation));
                }
            }

        } catch (Exception e) {
            // 静默处理异常
        }

        modelCache.put(cacheKey, elements);
        return elements;
    }

    /**
     * 将JsonArray转换为double数组
     * <p>
     * 将包含数字的JsonArray转换为double数组。
     * </p>
     * 
     * @param jsonArray JSON数组
     * @return double数组
     */
    private static double[] jsonArrayToDoubleArray(JsonArray jsonArray) {
        double[] array = new double[jsonArray.size()];
        for (int i = 0; i < jsonArray.size(); i++) {
            array[i] = jsonArray.get(i).getAsDouble();
        }
        return array;
    }

    // ==================== 坐标变换方法 ====================

    /**
     * 根据方块朝向变换点坐标
     * <p>
     * 根据方块的朝向对点坐标进行变换，以确保碰撞箱在不同朝向下正确显示。
     * </p>
     * 
     * @param point 原始点坐标 [x, y, z]
     * @param facing 方块朝向
     * @return 变换后的点坐标 [x, y, z]
     */
    private static double[] transformPoint(double[] point, Direction facing) {
        double x = point[0];
        double y = point[1];
        double z = point[2];

        // 根据朝向进行坐标变换
        return switch (facing) {
            case SOUTH -> new double[]{16 - x, y, 16 - z};
            case WEST -> new double[]{z, y, 16 - x};
            case EAST -> new double[]{16 - z, y, x};
            case UP -> new double[]{x, 16 - z, y};
            case DOWN -> new double[]{x, z, 16 - y};
            default -> new double[]{x, y, z};
        };
    }

    // ==================== 缓存管理方法 ====================

    /**
     * 清除所有缓存（用于资源重载等场景）
     * <p>
     * 清除所有缓存数据，包括模型缓存、形状缓存和BlockState缓存。
     * 在资源重载或开发调试时使用。
     * </p>
     */
    public static void clearAllCache() {
        modelCache.clear();
        shapeCache.clear();
        blockStateCache.clear();
    }

    // ==================== 数据类 ====================

    /**
     * 元素数据类
     * <p>
     * 用于存储模型元素的基本信息，包括起始点、结束点和是否包含旋转。
     * </p>
     */
    private record ElementData(double[] from, double[] to, boolean hasRotation) {
    }

    // ==================== 缓存生成器 ====================

    /**
     * 创建带缓存的形状生成器
     * <p>
     * 提供实例级缓存的形状生成器，用于特定方块的形状生成和缓存。
     * </p>
     */
    public static class CachedShapeGenerator {
        
        /**
         * 模组ID
         */
        public final String modId;
        
        /**
         * 模型路径
         */
        private final String modelPath;
        
        /**
         * 方块名称
         */
        private final String blockName;
        
        /**
         * 实例级缓存，只缓存当前方块的形状
         */
        private final Map<Direction, VoxelShape> instanceCache = new EnumMap<>(Direction.class);

        /**
         * 构造函数
         * <p>
         * 根据模组ID和模型路径创建缓存形状生成器。
         * 从模型路径自动推断方块名称。
         * </p>
         * 
         * @param modId 模组ID
         * @param modelPath 模型路径
         */
        public CachedShapeGenerator(String modId, String modelPath) {
            this.modId = modId;
            this.modelPath = modelPath;
            // 从模型路径推断方块名称
            int lastSlash = modelPath.lastIndexOf('/');
            int lastDot = modelPath.lastIndexOf('.');
            if (lastSlash >= 0 && lastDot > lastSlash) {
                this.blockName = modelPath.substring(lastSlash + 1, lastDot);
            } else {
                this.blockName = "unknown";
            }
        }

        /**
         * 构造函数
         * <p>
         * 根据模组ID、模型路径和方块名称创建缓存形状生成器。
         * </p>
         * 
         * @param modId 模组ID
         * @param modelPath 模型路径
         * @param blockName 方块名称
         */
        public CachedShapeGenerator(String modId, String modelPath, String blockName) {
            this.modId = modId;
            this.modelPath = modelPath;
            this.blockName = blockName;
        }

        /**
         * 获取指定方向的形状
         * <p>
         * 获取指定方向的VoxelShape，首先检查实例缓存，如果不存在则生成新形状。
         * </p>
         * 
         * @param direction 方向
         * @return VoxelShape 体素形状
         */
        public VoxelShape getShape(Direction direction) {
            // 首先检查实例缓存
            if (instanceCache.containsKey(direction)) {
                return instanceCache.get(direction);
            }

            // 使用全局缓存系统生成形状
            VoxelShape shape = createShapeFromModel(modId, modelPath, direction, blockName);
            instanceCache.put(direction, shape);
            return shape;
        }

        /**
         * 清除实例缓存
         * <p>
         * 清除当前生成器的实例缓存。
         * </p>
         */
        public void clearCache() {
            instanceCache.clear();
        }

        /**
         * 获取方块名称
         * <p>
         * 获取与当前生成器关联的方块名称。
         * </p>
         * 
         * @return 方块名称
         */
        public String getBlockName() {
            return blockName;
        }
    }
}